package com.linkhub.oms.common.util;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.util.ModifierUtil;
import cn.hutool.core.util.ReflectUtil;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
import java.util.function.Supplier;

/**
 * @Description: QtBean工具类
 * @Author: echo
 * @Date: 2020/11/29 21:10
 * @Version: 1.0
 */
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class QtBeanUtil {
    
    public static final CopyOptions DEFAULT_COPY_OPT = CopyOptions.create().ignoreError().ignoreNullValue();

    /**
     * 判断bean的所有属性(包括父类的属性)是否都为null, 默认去除static变量的判断
     *
     * @param bean           需要判断的对象
     * @param fieldPredicate 属性过滤条件, 为true则跳过,不进行null值判断
     * @return true: 都为null, false: 至少有一个不为null
     */
    public static boolean isEmpty(Object bean, Predicate<Field> fieldPredicate, Predicate<Field> isNull) {
        if (Objects.isNull(bean)) {
            return true;
        }
        Field[] fields = ReflectUtil.getFields(bean.getClass());
        for (Field field : fields) {
            if (ModifierUtil.isStatic(field) || Optional.ofNullable(fieldPredicate)
                    .map(predicate -> predicate.test(field))
                    .orElse(false)) {
                continue;
            }

            if (isNull.test(field)) {
                return false;
            }

        }

        return true;
    }

    /**
     * 判断Entity对象的属性是否都为null, 没有TableField/TableId注解，或者TableField的exist为false的跳过
     *
     * @param bean bean
     * @return true: 都为null, false: 至少有一个不为null
     */
    public static boolean isEmptyEntity(Object bean) {
        return isEmpty(bean, field -> !Optional.ofNullable(field.getAnnotation(TableField.class))
                        .map(TableField::exist)
                        .orElseGet(() -> field.getAnnotation(TableId.class) != null),
                field -> null != ReflectUtil.getFieldValue(bean, field));
    }

    /**
     * 设置空串字段为null
     *
     * @param bean bean
     */
    public static void setBlankFieldToNull(Object bean) {
        if (Objects.isNull(bean)) {
            return;
        }
        Field[] fields = ReflectUtil.getFields(bean.getClass());
        for (Field field : fields) {
            if (field.getType().isAssignableFrom(String.class)
                    && StringUtils.isBlank((CharSequence) ReflectUtil.getFieldValue(bean, field))) {
                ReflectUtil.setFieldValue(bean, field, null);
            }
        }

    }

    /**
     * 设置 {@code String} 类型的属性 null 值为 ""
     *
     * @param bean bean
     */
    public static void setStringNullFieldToBlank(Object bean) {
        formatBlankStringFields(bean, StringUtils.EMPTY);

    }

    /**
     * 格式化 null/"" {@code String} 类型的属性
     *
     * @param bean        bean
     * @param formatValue formatValue
     */
    public static void formatBlankStringFields(Object bean, String formatValue) {
        if (Objects.isNull(bean)) {
            return;
        }
        Field[] fields = ReflectUtil.getFields(bean.getClass());
        for (Field field : fields) {
            if (field.getType().isAssignableFrom(String.class)
                    && StringUtils.isBlank((CharSequence) ReflectUtil.getFieldValue(bean, field))) {
                ReflectUtil.setFieldValue(bean, field, formatValue);
            }
        }

    }


    public static void setNullToField(Object bean, String... fieldNames) {
        if (bean == null || fieldNames == null || fieldNames.length == 0) {
            return;
        }

        for (String fieldName : fieldNames) {
            Field field = ReflectUtil.getField(bean.getClass(), fieldName);
            if (Object.class.isAssignableFrom(field.getType())) {
                ReflectUtil.setFieldValue(bean, field, null);
            }
        }
    }

    /**
     * 复制Bean对象属性<br>
     * 限制类用于限制拷贝的属性，例如一个类我只想复制其父类的一些属性，就可以将editable设置为父类
     *
     * @param source 源Bean对象
     * @param target 目标Bean对象
     * @return 目标bean对象
     */
    public static <S, T> T copyProperties(S source, T target) {
        return copyProperties(source, target, DEFAULT_COPY_OPT, null);
    }

    /**
     * 复制Bean对象属性<br>
     * 限制类用于限制拷贝的属性，例如一个类我只想复制其父类的一些属性，就可以将editable设置为父类
     *
     * @param source   源Bean对象
     * @param target   目标Bean对象
     * @param consumer 自定义拷贝操作
     * @return 目标bean对象
     */
    public static <S, T> T copyProperties(S source, T target, BiConsumer<S, T> consumer) {
        return copyProperties(source, target, DEFAULT_COPY_OPT, consumer);
    }

    /**
     * 复制Bean对象属性<br>
     * 限制类用于限制拷贝的属性，例如一个类我只想复制其父类的一些属性，就可以将editable设置为父类
     *
     * @param source      源Bean对象
     * @param target      目标Bean对象
     * @param copyOptions 拷贝选项，见 {@link CopyOptions}
     * @return 目标bean对象
     */
    public static <S, T> T copyProperties(S source, T target, CopyOptions copyOptions) {
        return copyProperties(source, target, copyOptions, null);
    }

    /**
     * 复制Bean对象属性<br>
     * 限制类用于限制拷贝的属性，例如一个类我只想复制其父类的一些属性，就可以将editable设置为父类
     *
     * @param source      源Bean对象
     * @param target      目标Bean对象
     * @param copyOptions 拷贝选项，见 {@link CopyOptions}
     * @param consumer    自定义拷贝操作
     * @return 目标bean对象
     */
    public static <S, T> T copyProperties(S source, T target, CopyOptions copyOptions, BiConsumer<S, T> consumer) {
        BeanUtil.copyProperties(source, target, copyOptions);

        if (Objects.nonNull(consumer)) {
            consumer.accept(source, target);
        }

        return target;
    }

    /**
     * 复制Bean对象属性<br>
     *
     * @param sourceList   源Bean集合
     * @param targetNewObj 目标Bean对象的生成函数
     * @return 目标bean集合
     */
    public static <S, T> List<T> copyList(List<S> sourceList, Supplier<T> targetNewObj) {
        return copyList(sourceList, targetNewObj, DEFAULT_COPY_OPT, null);
    }

    /**
     * 复制Bean对象属性<br>
     *
     * @param sourceList   源Bean集合
     * @param targetNewObj 目标Bean对象的生成函数
     * @param consumer     自定义拷贝操作
     * @return 目标bean集合
     */
    public static <S, T> List<T> copyList(List<S> sourceList, Supplier<T> targetNewObj, BiConsumer<S, T> consumer) {
        return copyList(sourceList, targetNewObj, DEFAULT_COPY_OPT, consumer);
    }

    /**
     * 复制Bean对象属性<br>
     *
     * @param sourceList   源Bean集合
     * @param targetNewObj 目标Bean对象的生成函数
     * @param copyOptions  拷贝选项，见 {@link CopyOptions}
     * @param consumer     自定义拷贝操作
     * @return 目标bean集合
     */
    public static <S, T> List<T> copyList(List<S> sourceList, Supplier<T> targetNewObj, CopyOptions copyOptions, BiConsumer<S, T> consumer) {
        return QtStreamUtil.map(
                sourceList,
                source -> copyProperties(source, targetNewObj.get(), copyOptions, consumer));
    }
}
